package cn.memorynine.consumer.filter;


import cn.memorynine.common.enums.ResultEnum;
import cn.memorynine.common.model.ResponseWrapper;
import cn.memorynine.common.tool.JwtService;
import cn.memorynine.consumer.model.TokenModel;
import cn.memorynine.consumer.model.TokenRequestWrapper;
import cn.memorynine.dao.entity.Consumer;
import cn.memorynine.dao.mapper.ConsumerMapper;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * 2020-8-6 15:36:52
 * 过滤器：token校验
 */
@Slf4j
@WebFilter(urlPatterns = "*", filterName = "tokenFilter")
@Component
public class TokenFilter implements Filter {
    @Autowired
    private JwtService jwtService;

    @Autowired
    private ConsumerMapper consumerMapper;

    // URL白名单：允许直接通过的接口
    private static final List<String> URL_BLACK_LIST = new ArrayList<String>() {{
        add("/api/consumerLogin/*");
        add("/api/consumerLogup/*");
    }};

    @Override
    public void init( FilterConfig filterConfig ) throws ServletException {

    }

    @Override
    public void destroy() {

    }

    /**
     * URL白名单校验
     */
    private boolean checkUrlWhiteList( ServletRequest servletRequest ) {
        return checkUri(((HttpServletRequest) servletRequest).getRequestURI());
    }

    /**
     * token校验
     */
    private ResultEnum checkToken(ServletRequest servletRequest, TokenModel tokenModel ) {
        String token = ((HttpServletRequest) servletRequest).getHeader("token");
        log.info("token的值:" + token);
        if (StringUtils.isEmpty(token)) {
            return ResultEnum.TOKEN_NOT_EXISTS;
        }
        String tokenDecodeString;
        try {
            tokenDecodeString = jwtService.getTokenData(token);
        } catch (Exception e) {
            return ResultEnum.TOKEN_ERROR;
        }
        log.info("获得token解密信息：" + tokenDecodeString);
        TokenModel decodeTokenModel = JSONObject.parseObject(tokenDecodeString, tokenModel.getClass());

        BeanUtils.copyProperties(decodeTokenModel, tokenModel);
        return ResultEnum.SUCCESS;
    }

    /**
     * 获取ip地址
     */
    private String getIpAddress( HttpServletRequest request ) {
        if (request.getHeader("x-forwarded-for") == null) {
            return request.getRemoteAddr();
        }
        return request.getHeader("x-forwarded-for");
    }

    @SneakyThrows
    @Override
    public void doFilter( ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain ) {
        HttpServletResponse response = ((HttpServletResponse) servletResponse);
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        String ipAddress = getIpAddress(request);

        // 若：请求的是白名单
        if (checkUrlWhiteList(servletRequest)) {
            // 则：直接放行
            TokenRequestWrapper requestWrapper = new TokenRequestWrapper(request);
            Map<String, String[]> newMap = new HashMap<>(requestWrapper.getParameterMap());
            newMap.put("ipAddress", new String[]{ipAddress});
            requestWrapper.setParameterMap(newMap);
            TokenModel tokenModel = new TokenModel();
            requestWrapper.setTokenData(JSON.toJSONString(tokenModel));
            filterChain.doFilter(requestWrapper, servletResponse);
        } else {
            // 否，则：进行token校验
            TokenModel tokenModel = new TokenModel();
            ResultEnum resultEnum = checkToken(request, tokenModel);

            log.info("获取到解析的结果为："+JSONObject.toJSONString(tokenModel));

            // 判断校验结果
            switch (resultEnum) {
                case SUCCESS:
                    // 如果校验成功，则：解析token并附加参数
                    TokenRequestWrapper requestWrapper = new TokenRequestWrapper(request);
                    Map<String, String[]> newMap = new HashMap<>(requestWrapper.getParameterMap());
                    newMap.put("tokenConsumerAid", new String[]{String.valueOf(tokenModel.getTokenConsumerAid())});
                    requestWrapper.setParameterMap(newMap);
                    requestWrapper.setTokenData(JSON.toJSONString(tokenModel));

                    /**
                     * 校验管理员权限
                     */
                    //校验如果是要访问管理员端，必须为管理员才能放行，如果是访问用户端，则直接放行
                    String requestURI = request.getRequestURI();
                    String[] requestURISplit = requestURI.split("/");
                    if ("admin".equals(requestURISplit[2])){
                        //如果是要访问管理员端
                        Consumer consumer = consumerMapper.selectList(
                                Wrappers.<Consumer>lambdaQuery()
                                        .eq(Consumer::getAid, Integer.parseInt(String.valueOf(tokenModel.getTokenConsumerAid())))
                        ).get(0);
                        if (consumer.getStatusId() == 10){
                            //是管理员，放行
                            filterChain.doFilter(requestWrapper, response);
                            break;
                        } else {
                            response.setContentType("application/json; charset=utf-8");
                            response.setCharacterEncoding("UTF-8");
                            OutputStream outputStream = response.getOutputStream();
                            ResultEnum result = ResultEnum.API_DISABLE;
                            outputStream.write(JSONObject.toJSONString(ResponseWrapper.markError(resultEnum)).getBytes(StandardCharsets.UTF_8));
                            outputStream.flush();
                            return;
                        }
                    }

                    // 放行
                    filterChain.doFilter(requestWrapper, response);
                    break;
                default:
                    response.setContentType("application/json; charset=utf-8");
                    response.setCharacterEncoding("UTF-8");
                    OutputStream outputStream = response.getOutputStream();
                    outputStream.write(JSONObject.toJSONString(ResponseWrapper.markError(resultEnum)).getBytes(StandardCharsets.UTF_8));
                    outputStream.flush();
//                    outPutStreamWriter(response, resultEnum);
                    break;
            }
        }
    }

    /**
     * 验证uri是否满足白名单正则表达式
     *
     * @param uri 待验证的uri
     * @return 验证结果
     */
    private Boolean checkUri( String uri ) {
        Boolean action = false;
        for (String url : URL_BLACK_LIST) {
            //1. 将url中的*去掉
            url = url.replace("*","");
            //2. 使用正则表达式比对两个字符是否相等
            if(uri.startsWith(url)) {
                action = true;
            }
        }
        return action;
    }

    /**
     * 将封装好的信息返回给前端
     *
     * @param response   返回response
     * @param resultEnum 封装的返回对象
     */
    private static void outPutStreamWriter( HttpServletResponse response, ResultEnum resultEnum ) {
        PrintWriter writer = null;
        OutputStreamWriter osw = null;
        try {

            osw = new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8);
            writer = new PrintWriter(osw, true);
            writer.write(JSON.toJSONString(ResponseWrapper.markError(resultEnum)));
            writer.flush();
            writer.close();
            osw.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                writer.close();
            }
            if (osw != null) {
                try {
                    osw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
